package org.jc.reflect.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import org.jc.reflect.model.JField;
import org.jc.reflect.model.JMethod;

public class JReflectUtil {

	/**
	 * Get the methods that meets the requirements
	 * 
	 * @param clazz
	 * @return
	 */
	public static <T> List<JMethod> getMethods(Class<T> clazz) {
		return getMethods(clazz, false, false);
	}

	/**
	 * Get the methods that meets the requirements
	 * 
	 * @param clazz
	 * @param load_static
	 * @param load_private
	 * @return
	 */
	public static <T> List<JMethod> getMethods(Class<T> clazz, boolean load_static, boolean load_private) {
		List<JMethod> jMethods = new ArrayList<JMethod>();
		Method[] methods = clazz.getDeclaredMethods();
		JMethod jm = null;
		for (Method method : methods) {
			// filter out static methods.
			if (!load_static && Modifier.isStatic(method.getModifiers())) {
				continue;
			}
			// filter out private methods.
			if (!load_private && Modifier.isPrivate(method.getModifiers())) {
				continue;
			}
			String methodName = method.getName();
			Class<?>[] paramTypes = method.getParameterTypes();
			// System.out.println(methodName + " "+paramTypes);
			jm = new JMethod(methodName, paramTypes);
			jMethods.add(jm);
		}
		return jMethods;
	}

	/**
	 * Get the methods that meets the requirements
	 * 
	 * @param clazz
	 * @return
	 */
	public static <T> List<JField> getFields(Class<T> clazz) {
		return getFields(clazz, false);
	}

	/**
	 * Get the fields that meets the requirements
	 * 
	 * @param clazz
	 * @param load_static
	 * @return
	 */
	public static <T> List<JField> getFields(Class<T> clazz, boolean load_static) {
		List<JField> jFields = new ArrayList<JField>();
		Field[] fields = clazz.getDeclaredFields();
		JField jf = null;
		for (Field field : fields) {
			// filter out static fields.
			if (!load_static && Modifier.isStatic(field.getModifiers())) {
				continue;
			}
			String fieldName = field.getName();
			Class<?> type = field.getType();
			jf = new JField(fieldName, type);
			jFields.add(jf);
		}
		return jFields;
	}

	public static <T> Object invoke(T instance, JMethod jMethod, Object[] params) throws IllegalAccessException,
			IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		return invoke(instance, jMethod.getName(), jMethod.getParamTypes(), params);
	}

	public static <T> Object invoke(T instance, String method, Class<?>[] paramTypes, Object[] params)
			throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException,
			SecurityException {
		return instance.getClass().getMethod(method, paramTypes).invoke(instance, params);
	}

	public static <T> Object invokeGetField(T instance, String fieldName)
			throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
		return invokeGetField(instance, fieldName, true);
	}

	public static <T> Object invokeGetField(T instance, String fieldName, boolean accessible)
			throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
		Field field = instance.getClass().getDeclaredField(fieldName);
		field.setAccessible(accessible);
		Object value = field.get(instance);
		return value;
	}

	public static <T> void invokeSetField(T instance, String fieldName, Object value)
			throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		invokeSetField(instance, fieldName, value, true);
	}

	public static <T> void invokeSetField(T instance, String fieldName, Object value, boolean accessible)
			throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		Field field = instance.getClass().getDeclaredField(fieldName);
		field.setAccessible(accessible);
		field.set(instance, value);
	}
}
